﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net.Sockets;
using rkShared.BEncode;
using rkShared.Logging;
using System.IO.Compression;

namespace rkShared.Net
{
    public class MessageReader
    {
        public delegate void MessageReceivedEventHandler(MessageReader reader, NetMessage msg);
        public event MessageReceivedEventHandler MessageReceived;
        public event EventHandler SocketErrorOccured;

        private bool m_started = false;
        public Socket m_socket;

        public int READ_BUFFER_SIZE = 8192;

        public MessageReader(Socket socket)
        {
            m_socket = socket;
        }
        public void Start()
        {
            if (!m_started)
            {
                NetworkLogger.Log.InfoFormat("STARTING MESSAGEREADER THREAD. PROTOCOL TYPE={0}", m_socket.ProtocolType);
                ThreadPool.QueueUserWorkItem(this.StartAsync);
            }
        }
        public void Stop()
        {
            if (m_started)
            {
                try
                {
                    NetworkLogger.Log.InfoFormat("STOPPING MESSAGE READER");
                    m_started = false;
                    if (m_socket.ProtocolType == ProtocolType.Tcp)
                        m_socket.Disconnect(false);
                    m_socket.Close();
                }
                catch
                {
                }
            }
        }
        private void StartAsync(object state)
        {
            if (!m_started)
            {
                m_started = true;
                NetworkLogger.Log.InfoFormat("!!!MESSAGEREADER STARTED!!!. PROTOCOL TYPE={0}", m_socket.ProtocolType);
                while (m_started)
                {
                    try
                    {
                        MemoryStream temp = this.ReadBytes(m_socket, 4);    //Read length...

                        NetworkLogger.Log.Info("NEW MESSAGE RECEIVED");

                        int _length = BitConverter.ToInt32(temp.ToArray(), 0);
                        NetworkLogger.Log.InfoFormat("THE LENGTH OF MESSAGE IS {0} bytes", _length);

                        temp.Close();

                        temp = this.ReadBytes(m_socket, _length);
                        temp.Seek(0, SeekOrigin.Begin);
                        
                        NetMessage _msg = new NetMessage();
                        BEncodedDictionary _dict = new BEncodedDictionary();
                        
                        //GZipStream _zipStream = new GZipStream(temp, CompressionMode.Decompress);
                        BinaryReader _reader = new BinaryReader(temp);
                        _dict.Decode(_reader);

                        temp.Close();

                        _msg.DeserializeFromDictionary(_dict);
                        this.OnMessageReceived(_msg);
                    }
                    catch (Exception ex)
                    {
                        NetworkLogger.Log.Error("ERROR WHILE READING MESSAGE", ex);
                        //if not started, Stop call may caused this exception, so we do not generate the event.
                        if (m_started)
                            if (this.SocketErrorOccured != null)
                                this.SocketErrorOccured.BeginInvoke(this, EventArgs.Empty, this.EventFiredCallback, this.SocketErrorOccured);

                        if (!(ex is SocketException) && !(ex is ObjectDisposedException))
                            throw ex;
                    }
                }
            }
        }

        private byte[] Decompress(int length, GZipStream zipStream)
        {
            byte[] _result = new byte[length];
            zipStream.Read(_result, 0, length);
            return _result;
        }
        private MemoryStream ReadBytes(Socket s, int size)
        {
            MemoryStream _result = new MemoryStream();
            byte[] _buffer = new byte[READ_BUFFER_SIZE];

            int _bytesReceived, _remaining = size;
            while (_remaining > 0)
            {
                int _readSize = _remaining > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : _remaining;
                _bytesReceived = s.Receive(_buffer, 0, _readSize, SocketFlags.None);
                _remaining -= _bytesReceived;

                _result.Write(_buffer, 0, _bytesReceived);
            }
            return _result;
        }
        private void OnMessageReceived(NetMessage msg)
        {
            //we invoke the message received event asyncronouslyi to prevent unexpected UI blockings.
            if (this.MessageReceived != null)
                this.MessageReceived.BeginInvoke(this, msg, this.EventFiredCallback, this.MessageReceived);
        }
        /// <summary>
        /// Dummy callback function for asyncronousky fired events...
        /// </summary>
        /// <param name="ar"></param>
        private void EventFiredCallback(IAsyncResult ar)
        {
        }
    }
}
